package com.future.lock;

import com.future.util.RedissionDistributedLock;
import com.future.util.UnsafeInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import sun.misc.Unsafe;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.LockSupport;

@Service
public class Lock {

    Logger LOGGER = LoggerFactory.getLogger(Lock.class);

    //用于线程竞争资源状态，能拿到状态继续执行，没拿到放进队列
    private volatile int state = 0;

    //当前获取了锁的线程
    private Thread lockHolder;

    //队列(保证入队出队的安全性)
    //ConcurrentLinkedDeque (是一个基于链接节点的无界线程安全队列，它采用先进先出的规则对节点进行排序，当我们添加一个元素的时候，它会添加到队列的尾部，当我们获取一个元素时，它会返回队列头部的元素)
    private final ConcurrentLinkedQueue<Thread> queue = new ConcurrentLinkedQueue<>();

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public Thread getLockHolder() {
        return lockHolder;
    }

    public void setLockHolder(Thread lockHolder) {
        this.lockHolder = lockHolder;
    }


//-----------------------------------------------------------------------------------------------

    /**
     * 所有线程一起抢state =0 ，只有为0时表示当前锁是自由的可以抢
     * 多线程修改state只能保证一个线程成功，不能用synchronized关键字
     * 采用无锁方式(CAS)
     * CAS:指令执行的原子性
     */
    //例如 T1 , T2 ,T3 三个线程
    //加锁具体实现
    public boolean aquire() {
        Thread current = Thread.currentThread();
        int state = getState();
        if (state == 0) {//当前线程可以加锁
            //判断
            ConcurrentLinkedQueue<Thread> q = this.queue;
            //当一个线程进入后先判断队列中是否存在线程
            if ((q.size() == 0 || current == q.peek()) && compareAndSwapState(0, 1)) {
                //判断队列size为0，并且CAS加锁，加锁成功，预期值为0，修改为1，表示加锁
                //设置当前获取了锁的线程
                setLockHolder(current);
                return true;
            }
        }
        return false;
    }

    //加锁
    public void lock() {
        if (aquire()) {
            //T1加锁成功
            //进入的线程加锁成功
            return;
        }
        //加锁失败执行：
        //当前线程
        Thread current = Thread.currentThread();
        LOGGER.info("{}获取锁成功", current.getName());
        //放入队列排队
        queue.add(current);
        //没有拿到锁的线程，自旋，直到被唤醒并加锁成功
        for (; ; ) {
            //尝试进行加锁
            //T1释放锁，唤醒T2
            if ((queue.peek() == current) && aquire()) {//拿队列第一个线程,去加锁
                queue.poll(); //加锁成功线程被唤醒，T2从队列移除
                return;
            }
            //拿锁再次失败，当前线程阻塞
            LockSupport.park();
            //--------------------------------------
            //超时情况
            //--------------------------------------
        }
    }

//-----------------------------------------------------------------------------------------------


//-----------------------------------------------------------------------------------------------

    //解锁
    public void unlock() {
        //当前线程
        Thread current = Thread.currentThread();
        //当前获取了锁的线程
        //判断当前线程不等于当前获取了锁的线程，是不可以释放锁的
        if (current != getLockHolder()) {
            throw new RuntimeException("不可以释放锁");
        }
        if (compareAndSwapState(1, 0)) {//释放锁，state从1改回0
            //当前获取了锁的线程制空
            setLockHolder(null);
            LOGGER.info("{}释放锁成功，唤醒其他锁", current.getName());
            //唤醒队列中的队首线程
            Thread thread = queue.peek();
            if (thread != null) {
                LockSupport.unpark(thread);
            }
        }
    }

//-----------------------------------------------------------------------------------------------

    //CAS算法
    public final boolean compareAndSwapState(int expect, int update) {
        //参数说明
        //1.修改对象
        //2.偏移量
        //3.预期初始值
        //4.修改后的值
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

    //反射获取Unsafe类
    private static final Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();

    //定义偏移量
    private static final long stateOffset;

    //偏移量计算
    static {
        try {
            stateOffset = unsafe.objectFieldOffset(Lock.class.getDeclaredField("state"));
        } catch (Exception e) {
            throw new Error();
        }
    }

}
